/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import me.zhengjie.constant.ConstantUtils;
import me.zhengjie.enums.RedisKeyEnum;
import me.zhengjie.modules.system.domain.CourseClass;
import me.zhengjie.modules.system.domain.CourseClassRequest;
import me.zhengjie.modules.system.domain.CourseInfo;
import me.zhengjie.modules.system.domain.CourseSigninConfig;
import me.zhengjie.modules.system.domain.vo.CourseClassResp;
import me.zhengjie.modules.system.repository.CourseClassRepository;
import me.zhengjie.modules.system.service.CourseClassService;
import me.zhengjie.modules.system.service.CourseClassStudentService;
import me.zhengjie.modules.system.service.CourseClockConfigService;
import me.zhengjie.modules.system.service.dto.*;
import me.zhengjie.modules.system.service.mapstruct.CourseClassMapper;
import me.zhengjie.modules.web.service.course.impl.CourseServiceImpl;
import me.zhengjie.modules.web.service.mybatisplus.ICourseSigninConfigPlusService;
import me.zhengjie.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
* @website https://el-admin.vip
* @description 服务实现
* @author 王宇
* @date 2023-09-20
**/
@Service
@RequiredArgsConstructor
public class CourseClassServiceImpl implements CourseClassService {

    private final CourseClassRepository courseClassRepository;
    private final CourseClassMapper courseClassMapper;

    @Autowired
    private CourseClockConfigService courseClockConfigService;
    @Autowired
    private CourseClassStudentService courseClassStudentService;

    @Autowired
    private ICourseSigninConfigPlusService signinConfigPlusService;
    @Autowired
    private RedisUtils redisUtils;
    private static final Logger log = LoggerFactory.getLogger(CourseClassServiceImpl.class);

    @Override
    public Map<String,Object> queryAll(CourseClassQueryCriteria criteria, Pageable pageable){
        Page<CourseClass> page = courseClassRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        Map<String, Object> resultMap = PageUtil.toPage(page.map(courseClassMapper::toDto));
        if (MapUtil.isNotEmpty(resultMap) && resultMap.get("content") != null) {
            for (CourseClassDto courseClass : (List<CourseClassDto>)resultMap.get("content")) {
                LambdaQueryWrapper<CourseSigninConfig> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(CourseSigninConfig::getClassNo,courseClass.getClassNo());
                List<CourseSigninConfig> list = signinConfigPlusService.list(wrapper);
                if (CollectionUtil.isNotEmpty(list)){
                    List<CourseClassRequest.SignTime> signTimes = new ArrayList<>();
                    for (CourseSigninConfig courseSigninConfig : list) {
                        CourseClassRequest.SignTime signTime = new CourseClassRequest.SignTime();
                        signTime.setStartTime(courseSigninConfig.getStartTime());
                        signTime.setEndTime(courseSigninConfig.getEndTime());
                        signTimes.add(signTime);
                    }
                    courseClass.setSignTimeList(signTimes);
                }
            }
        }
        return resultMap;
    }

    @Override
    public List<CourseClassResp> getAll() {
        CourseClassQueryCriteria criteria = new CourseClassQueryCriteria();
        criteria.setStatus(ConstantUtils.Valid);
        List<CourseClassDto> list = queryAll(criteria);
        if (CollectionUtil.isEmpty(list)){
            return null;
        }
        list = list.stream().sorted(Comparator.comparing(CourseClassDto::getCreateTime).reversed()).collect(Collectors.toList());
        List<CourseClassResp> respList = new ArrayList<>();
        for (CourseClassDto classDTO : list) {
            CourseClassResp resp =  new CourseClassResp();
            resp.setClassNo(classDTO.getClassNo());
            resp.setName(classDTO.getName());
            resp.setCourseNo(classDTO.getCourseNo());
            CourseClockConfigQueryCriteria courseClockQueryCriteria = new CourseClockConfigQueryCriteria();
            courseClockQueryCriteria.setCourseNo(classDTO.getCourseNo());
            courseClockQueryCriteria.setStatus(ConstantUtils.Valid);
            List<CourseClockConfigDto> courseClockDtoList = courseClockConfigService.queryAll(courseClockQueryCriteria);
            if (CollectionUtil.isEmpty(courseClockDtoList)){
                resp.setUserNumber(0);
                respList.add(resp);
                continue;
            }
            CourseClockConfigDto courseClockDto = courseClockDtoList.get(0);
            resp.setStartDate(DateUtil.getTimeStamp(courseClockDto.getStartDate()));
            resp.setEndDate(DateUtil.getTimeStamp(courseClockDto.getEndDate()));
            resp.setUserNumber(classDTO.getStudentNumber());
            respList.add(resp);
        }
        return respList;
    }

    @Override
    public List<CourseClassDto> queryAll(CourseClassQueryCriteria criteria){
        List<CourseClassDto> courseClassDtos = courseClassMapper.toDto(courseClassRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
        if (CollectionUtil.isEmpty(courseClassDtos)){
            return null;
        }
        //查询学员
        courseClassDtos.parallelStream().forEach(s -> {
            CourseClassStudentQueryCriteria studentQueryCriteria = new CourseClassStudentQueryCriteria();
            studentQueryCriteria.setClassNo(s.getClassNo());
            List<CourseClassStudentDto> studentList = courseClassStudentService.queryAll(studentQueryCriteria);
            s.setStudentNumber(0);
            if (CollectionUtil.isNotEmpty(studentList)){
                s.setStudentNumber(studentList.size());
            }
        });
        return courseClassDtos;
    }

    @Override
    @Transactional
    public CourseClassDto findById(Long classId) {
        CourseClass courseClass = courseClassRepository.findById(classId).orElseGet(CourseClass::new);
        ValidationUtil.isNull(courseClass.getClassId(),"CourseClass","classId",classId);
        CourseClassDto courseClassDto = courseClassMapper.toDto(courseClass);

        LambdaQueryWrapper<CourseSigninConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseSigninConfig::getClassNo,courseClass.getClassNo());
        List<CourseSigninConfig> list = signinConfigPlusService.list(wrapper);
        if (CollectionUtil.isNotEmpty(list)){
            List<CourseClassRequest.SignTime> signTimes = new ArrayList<>();
            for (CourseSigninConfig courseSigninConfig : list) {
                CourseClassRequest.SignTime signTime = new CourseClassRequest.SignTime();
                signTime.setStartTime(courseSigninConfig.getStartTime());
                signTime.setEndTime(courseSigninConfig.getEndTime());
                signTimes.add(signTime);
            }
            courseClassDto.setSignTimeList(signTimes);
        }
        return courseClassDto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CourseClassDto create(CourseClass resources) {
        resources.setClassNo(UUID.fastUUID().toString(true));
        return courseClassMapper.toDto(courseClassRepository.save(resources));
    }

    @Override
    public CourseClassDto create(CourseClassRequest resources) {
        CourseClass courseClass = new CourseClass();
        BeanUtils.copyProperties(resources,courseClass);
        String classNo = UUID.fastUUID().toString(true);
        String currentUsername = SecurityUtils.getCurrentUsername();
        courseClass.setClassNo(classNo);
        courseClass.setCreateTime(DateUtil.TimestampNow());
        courseClass.setCreateby(currentUsername);
        CourseClass saveResult = courseClassRepository.save(courseClass);
        if (Objects.isNull(saveResult)){
            return null;
        }
        if (CollectionUtil.isEmpty(resources.getSignTimeList())){
            return courseClassMapper.toDto(saveResult);

        }
        for (CourseClassRequest.SignTime signTime : resources.getSignTimeList()) {
            CourseSigninConfig courseSigninConfig = new CourseSigninConfig();
            courseSigninConfig.setCourseNo(resources.getCourseNo());
            courseSigninConfig.setClassNo(classNo);
            courseSigninConfig.setStartTime(signTime.getStartTime());
            courseSigninConfig.setEndTime(signTime.getEndTime());
            courseSigninConfig.setCreateTime(DateUtil.TimestampNow());
            courseSigninConfig.setCreateby(currentUsername);
            signinConfigPlusService.save(courseSigninConfig);
        }
        return courseClassMapper.toDto(saveResult);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(CourseClass resources) {
        CourseClass courseClass = courseClassRepository.findById(resources.getClassId()).orElseGet(CourseClass::new);
        ValidationUtil.isNull( courseClass.getClassId(),"CourseClass","id",resources.getClassId());
        courseClass.copy(resources);
        courseClassRepository.save(courseClass);

        redisUtils.set(RedisKeyEnum.getClassInfoCourseNoKey(courseClass.getCourseNo()), courseClass);
        redisUtils.set(RedisKeyEnum.getClassInfoClassNoKey(courseClass.getClassNo()), courseClass);
    }

    @Override
    public void update(CourseClassRequest resources) {
        CourseClass courseClass = courseClassRepository.findById(resources.getClassId()).orElseGet(CourseClass::new);
        ValidationUtil.isNull( courseClass.getClassId(),"CourseClass","id",resources.getClassId());
        courseClass.copy(resources);
        courseClassRepository.save(courseClass);

        redisUtils.set(RedisKeyEnum.getClassInfoCourseNoKey(courseClass.getCourseNo()), courseClass);
        redisUtils.set(RedisKeyEnum.getClassInfoClassNoKey(courseClass.getClassNo()), courseClass);

        LambdaQueryWrapper<CourseSigninConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseSigninConfig::getClassNo,courseClass.getClassNo());
        signinConfigPlusService.remove(wrapper);
        if (CollectionUtil.isNotEmpty(resources.getSignTimeList())){
            for (CourseClassRequest.SignTime signTime : resources.getSignTimeList()) {
                CourseSigninConfig courseSigninConfig = new CourseSigninConfig();
                courseSigninConfig.setCourseNo(resources.getCourseNo());
                courseSigninConfig.setClassNo(courseClass.getClassNo());
                courseSigninConfig.setStartTime(signTime.getStartTime());
                courseSigninConfig.setEndTime(signTime.getEndTime());
                courseSigninConfig.setCreateTime(DateUtil.TimestampNow());
                signinConfigPlusService.save(courseSigninConfig);
            }
        }
    }

    @Override
    public void deleteAll(Long[] ids) {
        for (Long classId : ids) {
            courseClassRepository.deleteById(classId);
        }
    }

    @Override
    public void download(List<CourseClassDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (CourseClassDto courseClass : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("编辑编号", courseClass.getClassNo());
            map.put("班级名称", courseClass.getName());
            map.put("状态", courseClass.getStatus());
            map.put("创建时间", courseClass.getCreateTime());
            map.put("创建人", courseClass.getCreateby());
            map.put("更新时间", courseClass.getUpdateTime());
            map.put("更新人", courseClass.getUpdateby());
            map.put("座位图", courseClass.getSeatingMapImage());
            map.put("课程编号", courseClass.getCourseNo());
            map.put("餐费", courseClass.getMeals());
            map.put("是否需要审核打卡，0：不需要，1：需要", courseClass.getAuditClock());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public CourseClass getClassByCourseNo(String courseNo) {
        try {
            Object courseObj = redisUtils.get(RedisKeyEnum.getClassInfoCourseNoKey(courseNo));
            if (Objects.nonNull(courseObj)){
                return (CourseClass) courseObj;
            }
        } catch (Exception e) {
            log.error("缓存班级信息异常:{}",e);
        }
        CourseClass courseClass = courseClassRepository.findByCourseNo(courseNo);
        if (Objects.nonNull(courseClass)){
            boolean result = redisUtils.set(RedisKeyEnum.getClassInfoCourseNoKey(courseNo), courseClass);
            log.info("缓存班级信息结果:{},课程编号：{}",result,courseNo);
            return courseClass;
        }
        return null;
    }

    @Override
    public CourseClass getClassByClassNo(String classNo) {
        try {
            Object courseObj = redisUtils.get(RedisKeyEnum.getClassInfoClassNoKey(classNo));
            if (Objects.nonNull(courseObj)){
                return (CourseClass) courseObj;
            }
        } catch (Exception e) {
            log.error("缓存班级信息异常:{}",e);
        }
        CourseClass courseClass = courseClassRepository.findByClassNo(classNo);
        if (Objects.nonNull(courseClass)){
            boolean result = redisUtils.set(RedisKeyEnum.getClassInfoClassNoKey(classNo), courseClass);
            log.info("缓存班级信息结果:{},班级编号：{}",result,classNo);
            return courseClass;
        }
        return null;
    }
}